package searchlight;

import java.io.IOException;

import javax.microedition.io.Connector;
import javax.microedition.io.Datagram;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

import com.sun.spot.io.j2me.radiogram.RadiogramConnection;
import com.sun.spot.peripheral.Spot;
import com.sun.spot.peripheral.TimeoutException;
import com.sun.spot.resources.Resources;
import com.sun.spot.resources.transducers.ITriColorLEDArray;
import com.sun.spot.resources.transducers.LEDColor;
import com.sun.spot.service.BootloaderListenerService;

public class SearchLightApplication extends MIDlet {

    private final ITriColorLEDArray leds = (ITriColorLEDArray) Resources.lookup(ITriColorLEDArray.class);

    private final int broadcastPort = 128;

    private final int dutyCycle = 8;

    private final String probingScheme = "sequential";

    private final int slotSizeInMiliSec = 1000;

    private final long address = Spot.getInstance().getRadioPolicyManager().getIEEEAddress();

    private final long[] foundSpots = new long[1000];

    private int numberOfSpotFound = 0;

    private final int nodesToDiscover = 12;

    protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {

    }

    protected void pauseApp() {

    }

    protected void startApp() throws MIDletStateChangeException {
        BootloaderListenerService.getInstance().start();
        Spot.getInstance().getRadioPolicyManager().setOutputPower(-31);
        turnLedsOff();
        for (int i = 0; i < leds.size(); i++) {
            leds.getLED(i).setColor(LEDColor.BLUE);
        }

        for (int i = 0; i < foundSpots.length; i++) {
            foundSpots[i] = -1;
        }
        
        new Thread(new Runnable() {
            
            public void run() {

                while (true) {
                    if (nodesToDiscover == numberOfSpotFound) {
                        flashLight();
                    } else {
                        try {
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }).start();

        final ProbingScheme scheme = new ProbingScheme(dutyCycle, probingScheme);
        while (true) {
            final boolean[] nextPeriod = scheme.getNextPeriod();

            for (int i = 0; i < nextPeriod.length; i++) {
                if (nextPeriod[i]) {
                    doActiveSlot();
                } else {
                    doInactiveSlot();
                }
            }
        }
    }

    private void doActiveSlot() {
        turnLedsOn();

        broadcastHelloMessage();

        listenForHelloMessage();

        broadcastHelloMessage();

        turnLedsOff();
    }

    private void doInactiveSlot() {
        try {
            System.out.println("Sleeping");
            Thread.sleep(slotSizeInMiliSec);
        } catch (InterruptedException e) {
            System.out.println("Unexpected exception when sleeping");
            e.printStackTrace();
        }
    }

    private void broadcastHelloMessage() {
        RadiogramConnection broadcastConnection = null;
        try {
            broadcastConnection = (RadiogramConnection) Connector.open("radiogram://broadcast:" + broadcastPort);
            broadcastConnection.setMaxBroadcastHops(1);

            Datagram datagram = broadcastConnection.newDatagram(broadcastConnection.getMaximumLength());
            datagram.writeLong(address);

            broadcastConnection.send(datagram);

            System.out.println("Broadcasting");
        } catch (Exception e) {
            System.out.println("Unexpected exception when broadcasting");
            e.printStackTrace();
        } finally {
            if (broadcastConnection != null) {
                try {
                    broadcastConnection.close();
                } catch (IOException e) {
                    System.out.println("Unexpected exception when closing connection");
                    e.printStackTrace();
                }
            }
        }
    }

    private void listenForHelloMessage() {
        long listenDuration = 800;
        RadiogramConnection broadcastConnection = null;
        try {
            broadcastConnection = (RadiogramConnection) Connector.open("radiogram://:" + broadcastPort);
            Datagram datagram = broadcastConnection.newDatagram(broadcastConnection.getMaximumLength());

            System.out.println("Listening");

            while (listenDuration > 0) {
                final long start = System.currentTimeMillis();

                broadcastConnection.setTimeout(listenDuration);
                datagram.reset();

                broadcastConnection.receive(datagram);

                final long newSpot = datagram.readLong();

                boolean isAlreadyDiscovered = false;
                for (int i = 0; i < numberOfSpotFound; i++) {
                    if (foundSpots[i] == newSpot) {
                        isAlreadyDiscovered = true;
                        break;
                    }
                }
                if (!isAlreadyDiscovered) {
                    foundSpots[numberOfSpotFound] = newSpot;
                    numberOfSpotFound++;
                    System.out.println("Found " + newSpot + ". Total spots: " + numberOfSpotFound);
                }

                final long end = System.currentTimeMillis();

                listenDuration = listenDuration - (end - start);
            }
        } catch (TimeoutException e) {

        } catch (Exception e) {
            System.out.println("Unexpected exception when listening");
            e.printStackTrace();
        } finally {
            if (broadcastConnection != null) {
                try {
                    broadcastConnection.close();
                } catch (IOException e) {
                    System.out.println("Unexpected exception when closing connection");
                    e.printStackTrace();
                }
            }
        }
    }

    private void turnLedsOn() {
        for (int i = 0; i < leds.size(); i++) {
            leds.getLED(i).setOn();
        }
    }

    private void turnLedsOff() {
        for (int i = 0; i < leds.size(); i++) {
            leds.getLED(i).setOff();
        }
    }

    private void flashLight() {
        for (int i = 0; i < leds.size(); i++) {
            leds.getLED(i).setColor(LEDColor.GREEN);
        }
        
        for (int i = 0; i < leds.size(); i++) {
            leds.getLED(i).setOn();
        }

        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        for (int i = 0; i < leds.size(); i++) {
            leds.getLED(i).setOff();
        }

    }
}
